package com.example.demo.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.entity.po.EsPolicyPO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;

/**
 * @author: Bin.L
 * @date: 2021/7/16 0016 18:07
 * @Description:
 */

@Service
@Slf4j
public class ElasticSearchService {

    @Resource
    RestHighLevelClient restHighLevelClient;

    /**
     * create index and mapping
     *
     * @param indexName
     * @param mapping
     * @return
     */
    public boolean createIndex(String indexName, String mapping) {
        GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
        try {
            boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            if (!exists) {
                CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
                Settings settings = Settings.builder()
                        .put("index.number_of_shards", 1)
                        .put("index.number_of_replicas", 1)
                        .build();

                createIndexRequest.settings(settings).mapping(mapping, XContentType.JSON);
                restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
        } catch (Exception e) {
            log.error("create index mapping error: {}", e.getMessage());
            e.printStackTrace();
        }
        return true;
    }

    /**
     * delete Es Index
     *
     * @param indexName
     * @return
     */
    public boolean deleteIndex(String indexName) {
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        try {
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
            return delete.isAcknowledged();
        } catch (Exception e) {
            log.error("delete es index error: {}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * insert one doc
     *
     * @param indexName
     * @param docJson
     * @return
     */
    public boolean insertOne(String indexName, String docJson, String id) {
        IndexRequest request = new IndexRequest(indexName);
        request.timeout(TimeValue.timeValueSeconds(1));
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        if (StrUtil.isNotBlank(id)) {
            request.id(id);
        }
        request.source(docJson, XContentType.JSON);
        try {
            IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
            log.info("index add doc Response: {}", response.toString());
            if (response.status().getStatus() == RestStatus.CREATED.getStatus()) {
                return true;
            }
        } catch (Exception e) {
            log.error("index[{}] add doc error: {}", indexName, e.getMessage());
            e.printStackTrace();
        }

        return false;
    }

    /**
     * Determine whether the document exists
     *
     * @param indexName
     * @param id
     */
    public void isExistsDoc(String indexName, String id) {
        GetRequest getRequest = new GetRequest(indexName, id);
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        try {
            boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
            System.out.println(exists);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * id get doc
     *
     * @param indexName
     * @param id
     */
    public <T> T getIdDoc(String indexName, String id, Class<T> beanClass) {
        GetRequest getRequest = new GetRequest(indexName, id);
        try {
            GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            T object = JSONUtil.toBean(getResponse.getSourceAsString(), beanClass);
            System.out.println(getResponse.getSourceAsString());
            System.out.println(getResponse.getId());
            return object;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * id delete doc
     *
     * @param indexName
     * @param id
     * @return
     */
    public boolean deleteDocId(String indexName, String id) {
        DeleteRequest request = new DeleteRequest(indexName, id);
        request.timeout(TimeValue.timeValueSeconds(1));
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        try {
            DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
            if (deleteResponse.status().getStatus() == RestStatus.OK.getStatus()) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Save documents in batches
     *
     * @param indexName
     * @param list
     */
    public void batchInsertDoc(String indexName, List<EsPolicyPO> list) {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueSeconds(3));
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        for (EsPolicyPO po : list) {
            bulkRequest.add(new IndexRequest(indexName).id(po.getId()).source(JSONUtil.toJsonStr(po), XContentType.JSON));
        }
        try {
            BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            // 检查是否有失败 至少有一个执行失败时，返回true
            if (bulkResponse.hasFailures()) {
                log.error("Batch Save Doc is Error ...");
            }
            // 迭代所有操作的结果
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                // 判断操作是否失败
                if (bulkItemResponse.isFailed()) {
                    // 如果失败，则获取失败信息
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
                    log.error("ERROR: {}", JSONUtil.toJsonStr(failure));
                    // 其它业务处理
                    continue;
                }

                // 检索操作的响应（成功与否），可以是IndexResponse、UpdateResponse或DeleteResponse，都可以看作是DocWriteResponse实例
                DocWriteResponse itemResponse = bulkItemResponse.getResponse();
                switch (bulkItemResponse.getOpType()) {
                    case INDEX:    // 处理索引操作的响应
                    case CREATE:
                        IndexResponse indexResponse = (IndexResponse) itemResponse;
                        break;
                    case UPDATE:   // 处理更新操作的响应
                        UpdateResponse updateResponse = (UpdateResponse) itemResponse;
                        break;
                    case DELETE:   // 处理删除操作的响应
                        DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
